/*******************************************************************************
 * This file is part of blueprint4j www.blueprint4j.org.
 * 
 *     blueprint4j is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Lesser General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 * 
 *     blueprint4j is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU Lesser General Public License for more details.
 * 
 *     You should have received a copy of the GNU Lesser General Public License
 *     along with blueprint4j. If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
package blueprint4j.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Vector;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;

public class TransportStructure implements Transactable {

	private String name = null;
	private File archived = null;
	private static SimpleDateFormat dateformat = new SimpleDateFormat("yyyyMMdd");
	private static SimpleDateFormat timeformat = new SimpleDateFormat("HHmmss");
	public File file[] = null;
	public String file_name [] = null;
	public HashMap extra = new HashMap();

	private static final String INFO_NAME = "info",HASHMAP_NAME="hashmap";

	private static File ARCHIVE_PATH = new File(Settings.getString("file.archive.path","ARCHIVE"));
	private static File BACKUP_PATH = new File(ARCHIVE_PATH.getAbsolutePath()+"/BACKUP");
	public File LOCAL_ARCHIVE_PATH = null;

	static {
		ARCHIVE_PATH.mkdir();
		BACKUP_PATH.mkdir();
	}

	public TransportStructure(String name) {
		this.name = name;
		LOCAL_ARCHIVE_PATH = new File(ARCHIVE_PATH.getAbsolutePath() + "/" + name);
		LOCAL_ARCHIVE_PATH.mkdir();
	}

	/**
	 * Creates a ZIP file of all data inside
	 */
	public File archive()
	throws BindException {
		try {
			JarEntry info = new JarEntry( INFO_NAME );
			JarEntry hashmap = new JarEntry( HASHMAP_NAME );
			JarEntry t_file[] = new JarEntry[file.length];
			System.out.println("File Length is " + file.length);
			for (int i =0;i < file.length;i++) {
				System.out.println("Filename = " + file_name[i]);
				t_file[i] = new JarEntry(file_name[i]);
			}
			File tmp = new File(LOCAL_ARCHIVE_PATH.getAbsolutePath()+"/"+Utils.getUniqueFileName("transport","defalte",new File[]{ARCHIVE_PATH,BACKUP_PATH}));
			FileOutputStream outstream = new FileOutputStream(tmp);
			JarOutputStream stream = new JarOutputStream ( outstream );
			stream.putNextEntry(info);
			for (int i = 0;i < file_name.length;i++) {
				System.out.println("WRITE DFILENAME AS " + file_name[i]); 
				stream.write((file_name[i] + "\n").getBytes());
			}
			stream.putNextEntry(hashmap);
			new ObjectOutputStream(stream).writeObject(extra);
			for (int i = 0;i < t_file.length;i++) {
				stream.putNextEntry(t_file[i]);
				Utils.readWrite(new FileInputStream(file[i]),stream,false);
			}
			stream.finish();
			stream.close();
			outstream.close();
			System.out.println("Archived -= " + tmp.getAbsolutePath());
			return tmp;
		} catch (Throwable throwable) {
			throw new BindException(throwable);
		}
	}
	
	public File getFile() {return archived;}
	
	public File addFile(File p_file,String p_filename) 
	throws BindException {
		System.out.println("ADD File = " + p_file.getName() + " " + p_file.getAbsolutePath());
		if (archived == null) {
			throw new BindException("First archive then add files");
		}
		File new_file[] = new File[file.length+1];
		String new_file_name[] = new String[file_name.length+1];
		for (int i = 0;i < file.length;i++) {
			new_file[i] = file[i];
			new_file_name[i] = file_name[i];
		}
		new_file[file.length] = p_file;
		new_file_name[file.length] = p_filename;
		file = new_file;
		file_name = new_file_name;
		File new_archive = archive();
		archived.delete();
		archived = new_archive;
		return archived;
	}

	/**
	 * This loads any file that is the 
	 */
	public static TransportStructure load(String name)
	throws BindException {
		TransportStructure transport = new TransportStructure(name);
		File file = new VectorFile(new File(transport.LOCAL_ARCHIVE_PATH.getAbsolutePath()).listFiles()).getLastModified();
		if (file !=null && file.renameTo(file)) {
			file.setLastModified(new Date().getTime());
			transport.load(file);
			return transport;
		}
		/*
		File list[] = new File(transport.LOCAL_ARCHIVE_PATH.getAbsolutePath()).listFiles();
		if (list != null && list.length > 0 && list[0].renameTo(list[0])) {
			transport.load(list[0]);
			return transport;
		}*/
		return null;
	}

	/**
	 * Loads the file from the archive
	 */
	private void load(File load_file)
	throws BindException {
		try{
			JarFile jar_file = new JarFile(load_file);
			Enumeration e = jar_file.entries();
			Vector v_fn = new Vector(),v_f = new Vector();
			for (;e.hasMoreElements();) {
				JarEntry entry = (JarEntry)e.nextElement();
				File tmp = File.createTempFile("transport_deflate",entry.getName());
				tmp.deleteOnExit();
				FileOutputStream stream = new FileOutputStream(tmp);
				Utils.readWrite(jar_file.getInputStream(entry),stream);
				if (INFO_NAME.equals(entry.getName())) {
					LineNumberReader reader = new LineNumberReader(new InputStreamReader(new FileInputStream(tmp)));
					for (String line = reader.readLine();line != null;line = reader.readLine()) {
						v_fn.add(line);
					}
				} else if (HASHMAP_NAME.equals(entry.getName())) {
					extra = (HashMap) new ObjectInputStream(new FileInputStream(tmp)).readObject();
				} else {
					v_f.add(tmp);
				}
			}
			file = new File[v_f.size()];
			file_name = new String[v_fn.size()];
			for (int i =0;i < v_f.size();i++) {
				file[i] = (File) v_f.get(i);
				file_name[i] = (String) v_fn.get(i);
			}
			jar_file.close();
			archived = load_file;
		} catch (Throwable th) {
			throw new BindException(th);
		}
	}

	public void rollback() throws BindException {}
	
	public static boolean restore(String reference_dir,String filename) {
		File backup = new File(BACKUP_PATH+"/"+filename);
		if (reference_dir == null) {
			reference_dir = "";
		}
		if (reference_dir.length() > 0) {
			new File(reference_dir).mkdir();
			filename = reference_dir + "/"+filename;
		}
		System.out.println("Restore = " + new File(ARCHIVE_PATH+"/"+filename).getAbsolutePath());
		System.out.println("Backuyp exiosts = " + backup.exists() + "  " + backup.getAbsolutePath() + " " + backup.renameTo(new File(ARCHIVE_PATH+"/"+filename))); 
		return (backup.exists() && backup.renameTo(new File(ARCHIVE_PATH+"/"+filename)));
	}
	
	public boolean backup(){
		// THIS WILL MOVE FILE TO BACKUP
		try {
			return archived.renameTo(new File(BACKUP_PATH.getAbsolutePath()+"/"+archived.getName()));
		} finally {
			archived = null;
		}
	}

	/**
	* Message is deleted from SOURCE 
	*/
	public void commit() throws BindException {
		if (archived != null) {
			archived.delete();
		}
	}

}

